package thread;

import net.jcip.annotations.GuardedBy;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.concurrent.*;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

public class LogService {
    private final ExecutorService exec = Executors.newSingleThreadExecutor();
    private final BlockingQueue<String> queue;
    private final LoggerThread loggerThread;
    private final PrintWriter writer;
    @GuardedBy("this")
    private boolean isShutdown;
    @GuardedBy("this")
    private int reservations;

    public LogService(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>();
        this.loggerThread = new LoggerThread();
        this.writer = new PrintWriter(writer);
    }

    public void start() {
        //  loggerThread.start();
    }

    public void stop() throws InterruptedException {
        try {
            exec.shutdown();
            exec.awaitTermination(1000, NANOSECONDS);
        } finally {
            writer.close();
        }
    }


    public void log(String msg) throws InterruptedException {
        try {
            exec.execute(new WriteTask(msg));
        } catch (RejectedExecutionException ignored) {

        }

    }

    private class WriteTask implements Runnable {
        private String msg;

        public WriteTask(String msg) {
            this.msg = msg;
        }


        @Override
        public void run() {
            try {
                synchronized (LogService.this) {
                    if (isShutdown) {
                        throw new IllegalStateException();
                    }
                    ++reservations;
                }
                queue.put(msg);

            } catch (InterruptedException interruptedException) {
                synchronized (LogService.this) {
                    isShutdown = true;
                }
                loggerThread.interrupt();
                System.out.println("ExecutorService is shut down");
            }
        }
    }

    private class LoggerThread extends Thread {
        public void run() {
            try {
                while (true) {
                    try {
                        synchronized (LogService.this) {
                            if (isShutdown && reservations == 0) {
                                break;
                            }
                            String msg = queue.take();
                            synchronized (LogService.this) {
                                --reservations;
                            }
                            writer.println(msg);
                        }
                    } catch (InterruptedException e) { /* retry */
                    }
                }
            } finally {
                writer.close();
            }
        }
    }
}
